﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Input;
using System.ComponentModel;
using System.Linq;
using MachineQA.DataAccess;
using MachineQA.Properties;
using MachineQA.Model;

namespace MachineQA.ViewModel
{
    public class MachinesViewModel : WorkspaceViewModel
    {
        readonly MachineRepository _machineRepository;
        MachineViewModel _selectedMachine;
        Machine _newMachine;
        ObservableCollection<MachineViewModel> _allMachines;
        ObservableCollection<LocationViewModel> _availableLocations;

        public MachinesViewModel(MachineRepository machineRepository)
        {
            base.DisplayName = Resources.MachinesViewModel_DisplayName;
            _machineRepository = machineRepository;
            _newMachine = new Machine();
            // Subscribe for notifications when new machines are added.
            _machineRepository.MachineAddedOrRemoved += this.OnMachineAddedOrRemovedFromRepository;
            this.CreateAllMachines();
            this.CreateAllAvailableLocations();
        }

        void CreateAllMachines()
        {
            List<MachineViewModel> all =
                (from mach in _machineRepository.GetMachines()
                 select new MachineViewModel(mach, _machineRepository)).ToList();

            foreach (MachineViewModel mvm in all)
                mvm.PropertyChanged += this.OnMachineViewModelPropertyChanged;

            _allMachines = new ObservableCollection<MachineViewModel>(all);
            _allMachines.CollectionChanged += this.OnCollectionChanged;
        }

        void CreateAllAvailableLocations()
        {
            LocationRepository temp = new LocationRepository(ref Constants.entities);
            List<LocationViewModel> all =
                (from loc in temp.GetLocations()
                 select new LocationViewModel(loc, temp)).ToList();
            _availableLocations = new ObservableCollection<LocationViewModel>(all);
        }

        #region Public Interface

        public ObservableCollection<MachineViewModel> AllMachines
        {
            get
            {
                if(_allMachines == null)
                {
                    List<MachineViewModel> all = (from mach in _machineRepository.GetMachines()
                                                  select new MachineViewModel(mach, _machineRepository)).ToList();
                    _allMachines = new ObservableCollection<MachineViewModel>(all);
                }
                return _allMachines;
            }
            private set
            {
                _allMachines = value;
            }
        }

        public ObservableCollection<LocationViewModel> AvailableLocations
        {
            get
            {
                // Get data fresh each time in case it changes
                List<LocationViewModel> all = (from loc in Constants.entities.Locations.ToList()
                                      select new LocationViewModel(loc,
                                          new LocationRepository(ref Constants.entities))).ToList();
                _availableLocations = new ObservableCollection<LocationViewModel>(all);
                return _availableLocations;
            }
            private set
            {
                _availableLocations = value;
            }
        }

        public MachineViewModel SelectedMachine
        {
            get
            {
                return _selectedMachine;
            }
            set
            {
                _selectedMachine = value;
            }
        }

        public MachineViewModel NewMachine
        {
            get
            {
                return new MachineViewModel(_newMachine, _machineRepository);
            }
            set
            {
                _newMachine.Name = value.Name;
            }
        }

        #endregion // Public Interface

        #region Base class overrides

        protected override void OnDispose()
        {
            // Dispose of all elements and clear list
            foreach (MachineViewModel machineVM in this.AllMachines)
                machineVM.Dispose();

            this.AllMachines.Clear();

            // Unregister event handlers
            this.AllMachines.CollectionChanged -= this.OnCollectionChanged;
            _machineRepository.MachineAddedOrRemoved -= this.OnMachineAddedOrRemovedFromRepository;
        }

        #endregion // Base class overrides
       
        #region Event Handling

        void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null && e.NewItems.Count != 0)
                foreach (MachineViewModel machineVM in e.NewItems)
                    machineVM.PropertyChanged += this.OnMachineViewModelPropertyChanged;

            if (e.OldItems != null && e.OldItems.Count != 0)
                foreach (MachineViewModel machineVM in e.OldItems)
                    machineVM.PropertyChanged -= this.OnMachineViewModelPropertyChanged;
        }

        void OnMachineViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            string IsSelected = "IsSelected";

            // Debugging only verification
            (sender as MachineViewModel).VerifyPropertyName(IsSelected);

            if (e.PropertyName == IsSelected)
            {
                this.OnPropertyChanged("IsSelected");
                _selectedMachine = AllMachines.Where((i) => i.IsSelected).First();
            }
        }

        void OnMachineAddedOrRemovedFromRepository(object sender, MachineAddedOrRemovedEventArgs e)
        {
            if (e.Added)
            {
                var viewModel = new MachineViewModel(e.NewMachine, _machineRepository);
                this.AllMachines.Add(viewModel);
            }
            else if (AllMachines.Where(i => i.Name == e.NewMachine.Name).Count() > 0)
                AllMachines.Remove(AllMachines.Where(i => i.Name == e.NewMachine.Name).First());
        }

        #endregion // Event handling

        RelayCommand _saveCommand;

        /// <summary>
        /// Returns a command that saves the machine.
        /// </summary>
        public ICommand SaveCommand
        {
            get
            {
                if (_saveCommand == null)
                {
                    _saveCommand = new RelayCommand(
                        param => this.Save(),
                        null
                        );
                }
                return _saveCommand;
            }
        }

        void Save()
        {
            _machineRepository.AddMachine(_newMachine);
            _newMachine = new Machine(); // Prepare for another new item
        }

        RelayCommand _deleteCommand;

        public ICommand DeleteCommand
        {
            get
            {
                if (_deleteCommand == null)
                {
                    _deleteCommand = new RelayCommand(
                        param => this.Delete(),
                        null
                        );
                }
                return _deleteCommand;
            }
        }

        void Delete()
        {
            if (_selectedMachine != null && _machineRepository.GetMachines().
                Where(i => i.Name == _selectedMachine.Name).Count() > 0)
            {
                int preCount = AllMachines.Count();
                _machineRepository.RemoveMachine(_machineRepository.GetMachines().
                    Where(i => i.Name == _selectedMachine.Name).First());
            }
        }
    }
}
